Exercise: From Class to Dataclass
Convert the following classes into dataclasses such that the initializers that the dataclass generates have the same behavior as the regular class:
class A:
def __init__(self) -> None:
self._length = 0
class B:
def __init__(self, x: int, y: str = "hello", values: list[int] | None = None) -> None:
self.x = x
self.y = y
self.values = [] if not values else values
class C:
def __init__(self, a: int = 3) -> None:
self.a = a
self.b = a + 3
def __init__(self) -> None:
self._length = 0
class B:
def __init__(self, x: int, y: str = "hello", values: list[int] | None = None) -> None:
self.x = x
self.y = y
self.values = [] if not values else values
class C:
def __init__(self, a: int = 3) -> None:
self.a = a
self.b = a + 3
Compatible Python Versions: 3.9+
Hi this is my solution :
@dataclass
class A:
_length: int = field(default=0,init=False)
@dataclass
class B:
x: int
y: str = "Hello"
l: list[int] = field(default_factory=list)
@dataclass
class C:
a: int = 3
b: int = field(init=False)
def __post_init__(self):
self.b = self.a + 3
@dataclass
class Customer:
name: str
address: str
email: str
@dataclass
class Phone:
brand: str
model: str
price: float = field(default=0)
serial_number: str
@dataclass
class Plan:
customer: Customer
phone: Phone
start_date: datetime = field(default_factory=datetime.now)
months: int = 12
monthly_price: float = 0.0
phone_included: bool
Looks good! I am a bit curious about the
price: float = field(default=0)part. Does your Linter complain about this? Because the type would be inferred to be an integer, but it is overridden as a float.Not really, but I disabled the linter because it was a little annoying in my work. (Sorry for my poor English.
)
I get that! But I highly suggest that you use a linter with strict type annotations. It will be a bit painful in the beginning, but in the end, it will benefit you a lot!
ok thanks
Exercise 1:
```
from dataclasses import dataclass, field
@dataclass
class A:
_length: int = field(default=0, init=False)
@dataclass
class B:
x: int
y: str = "hello"
l: list[int] = field(default_factory=list)
@dataclass
class C:
a: int = field(default=3)
b: int = field(init=False)
def __post_init__(self) -> None:
self.b = self.a + 3
```
Exercise 2:
```
from dataclasses import dataclass, field
from datetime import datetime
from random import randint
def date_today() -> str:
return datetime.now().strftime("%Y-%m-%d")
def generate_serial() -> str:
return datetime.now().strftime("%Y%m%d") + str(randint(0, 100000))
@dataclass
class Address:
building_number: int
street_name: str
unit_number: int | None
city: str
state: str
zip_code: int
country: str
@dataclass
class Customer:
first_name: str
last_name: str
address: Address
email: str
@dataclass
class Phone:
brand: str
model: str
price: int
serial_number: str = field(init=False, default_factory=generate_serial)
@dataclass
class Plan:
customer: Customer
phone: Phone
start_date: str = field(init=False, default_factory=date_today)
contract_length: int
monthly_price: int
phone_included: bool = False
```
Nice solution Scott! This looks great :)
Exercise 1: Is this correct?
from dataclasses import dataclass, field
from typing import List, Optional
@dataclass
class A:
_length: int = field(default=0, init=False)
@dataclass
class B:
x: int
y: str = "hello"
l: Optional[List[int]] = field(default_factory=list)
@dataclass
class C:
a: int = 3
b: int = field(init=False)
def __post_init__(self):
self.b = self.a + 3
Looks good! Only a minor remark, you can use the built-in
listas a type instead of using the typing module. Furthermore, you can use the | operator instead ofOptionalExercise 2:
from dataclasses import dataclass
from datetime import datetime
@dataclass
class Customer:
name: str
address: str
email_address: str
@dataclass
class Phone:
brand: str
model: str
price: float
serial_number: str
@dataclass
class Plan:
customer: Customer
phone: Phone
contract_start_date: datetime
total_contract_months: int
monthly_price: float
phone_included_in_contract: bool
Looks good! Nice work with the exercise!
Exercise 1:
from dataclasses import dataclass, field
@dataclass
class A:
_length: str
@dataclass
class B:
x: int
y: str = "hello"
l: list[int] = field(default_factory=list)
@dataclass
class C:
b: int
a: int = field(default=3)
def __post_init__(self):
self.b = self.a + 3
In Class C, code worked only when I moved the attribude b to the begin or I had to set a default value to b. Which one do you think is a better option?
I would say, follow the convention of Python of having arguments with not defaults first